// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-11-12

package conf

import (
	"gitee.com/go-libs/config"
	"gitee.com/go-libs/log/base"
	"net"
	"os"
	"sync"
)

var (
	once      = new(sync.Once)
	singleton *Config
)

const (
	defaultServiceName    = "logging"
	defaultServiceVersion = "0.0.0"
	defaultServiceHost    = "0.0.0.0"
	defaultServicePort    = 0000
)

// Config
// 系统配置.
type Config struct {
	Adapter    base.Adapter    `yaml:"adapter" json:"adapter,omitempty"`
	Level      base.Level      `yaml:"level" json:"level,omitempty"`
	TimeFormat base.TimeFormat `yaml:"time-format" json:"time_format,omitempty"`
	Tracer     base.Tracer     `yaml:"tracer" json:"tracer,omitempty"`

	// 日志适配
	FileAdapter    *FileAdapterConfig    `yaml:"file-adapter"`
	ElasticAdapter *ElasticAdapterConfig `yaml:"elastic-adapter"`
	KafkaAdapter   *KafkaAdapterConfig   `yaml:"kafka-adapter"`
	TermAdapter    *TermAdapterConfig    `yaml:"term-adapter"`

	// 追踪适配.

	JaegerTracer *JaegerTracerConfig `yaml:"jaeger-tracer"`
	ZipkinTracer *ZipkinTracerConfig `yaml:"zipkin-tracer"`

	// 链路适配.
	OpenTracingSampled      string `yaml:"opentracing-sampled"`
	OpenTracingSpanId       string `yaml:"opentracing-span-id"`
	OpenTracingSpanPosition string `yaml:"opentracing-span-position"`
	OpenTracingTraceId      string `yaml:"opentracing-trace-id"`

	// 动态计算.
	Pid              int      `yaml:"-" json:"pid"`
	ServiceAddresses []string `yaml:"-" json:"service_addresses,omitempty"`
	ServiceHost      string   `yaml:"-" json:"service_host,omitempty"`
	ServicePort      int      `yaml:"-" json:"service_port,omitempty"`
	ServiceName      string   `yaml:"-" json:"service_name,omitempty"`
	ServiceVersion   string   `yaml:"-" json:"service_version,omitempty"`

	// 级别状态.
	debugOn, infoOn, warnOn, errorOn, fatalOn bool
	done                                      bool
}

// New
// 配置单例.
func New() *Config { return singleton }

func (o *Config) SetLevel(name string) {
	o.Level = base.Level(name)
	o.executeLevel()
}

// After
// 后置处理, 从 YAML 解析完成之后触发.
func (o *Config) After() {
	if o.done {
		o.done = false
		o.executeLevel()
		o.executeTimeFormat()

		o.executeService()
		o.executeAddresses()
		o.executeDynamic()

		o.executeOpenTracing()
		o.executeAdapter()
		o.executeTracer()
	}
}

func (o *Config) DebugOn() bool { return o.debugOn }
func (o *Config) InfoOn() bool  { return o.infoOn }
func (o *Config) WarnOn() bool  { return o.warnOn }
func (o *Config) ErrorOn() bool { return o.errorOn }
func (o *Config) FatalOn() bool { return o.fatalOn }

// +---------------------------------------------------------------------------+
// | Access methods                                                            |
// +---------------------------------------------------------------------------+

func (o *Config) executeAdapter() {
	o.Adapter = o.Adapter.Defaulter()

	// Elastic 配置.
	if o.ElasticAdapter == nil {
		o.ElasticAdapter = &ElasticAdapterConfig{}
	}
	o.ElasticAdapter.init()

	// 文件配置.
	if o.FileAdapter == nil {
		o.FileAdapter = &FileAdapterConfig{}
	}
	o.FileAdapter.init()

	// Kafka 配置.
	if o.KafkaAdapter == nil {
		o.KafkaAdapter = &KafkaAdapterConfig{}
	}
	o.KafkaAdapter.init()

	// 终端配置.
	if o.TermAdapter == nil {
		o.TermAdapter = &TermAdapterConfig{}
	}
	o.TermAdapter.init()

}

func (o *Config) executeAddresses() {
	var (
		err  error
		list []net.Addr
	)

	o.ServiceAddresses = make([]string, 0)

	// Networking interfaces detect.
	if list, err = net.InterfaceAddrs(); err == nil {
		for _, address := range list {
			if v, ok := address.(*net.IPNet); ok {
				if v4 := v.IP.To4(); v4 != nil && !v.IP.IsLoopback() {
					o.ServiceAddresses = append(o.ServiceAddresses, v4.String())
				}
			}
		}
	}
}

func (o *Config) executeDynamic() {
	o.Pid = os.Getpid()
}

func (o *Config) executeLevel() {
	o.Level = o.Level.Defaulter()
	o.debugOn, o.infoOn, o.warnOn, o.errorOn, o.fatalOn = o.Level.DebugOn(), o.Level.InfoOn(), o.Level.WarnOn(), o.Level.ErrorOn(), o.Level.FatalOn()
}

func (o *Config) executeOpenTracing() {
	if o.OpenTracingSampled == "" {
		o.OpenTracingSampled = base.OpenTracingSampled
	}
	if o.OpenTracingSpanId == "" {
		o.OpenTracingSpanId = base.OpenTracingSpanId
	}
	if o.OpenTracingSpanPosition == "" {
		o.OpenTracingSpanPosition = base.OpenTracingSpanPosition
	}
	if o.OpenTracingTraceId == "" {
		o.OpenTracingTraceId = base.OpenTracingTraceId
	}
}

func (o *Config) executeService() {
	cfg := &struct {
		Host    string `yaml:"host"`
		Port    int    `yaml:"port"`
		Name    string `yaml:"name"`
		Version string `yaml:"version"`
	}{}

	if err := config.Seek("app.yaml", "app.yml").ScanYaml(cfg); err == nil {
	}

	// 服务地址.
	if cfg.Host != "" {
		o.ServiceHost = cfg.Host
	} else {
		o.ServiceHost = defaultServiceHost
	}

	// 服务端口.
	if cfg.Port > 0 {
		o.ServicePort = cfg.Port
	} else {
		o.ServicePort = defaultServicePort
	}

	// 服务名称.
	if cfg.Name != "" {
		// 来自配置.
		o.ServiceName = cfg.Name
	} else {
		// 默认名称.
		o.ServiceName = defaultServiceName
	}

	// 服务版本.
	if cfg.Version != "" {
		// 来自配置.
		o.ServiceVersion = cfg.Version
	} else {
		// 默认版本.
		o.ServiceVersion = defaultServiceVersion
	}
}

func (o *Config) executeTimeFormat() {
	o.TimeFormat = o.TimeFormat.Defaulter()
}

func (o *Config) executeTracer() {
	o.Tracer = o.Tracer.Defaulter()

	if o.JaegerTracer == nil {
		o.JaegerTracer = &JaegerTracerConfig{}
	}
	o.JaegerTracer.init()

	if o.ZipkinTracer == nil {
		o.ZipkinTracer = &ZipkinTracerConfig{}
	}
	o.ZipkinTracer.init()
}

func (o *Config) init() *Config {
	_ = config.Seek("log.yaml", "log.yml").ScanYaml(o)
	return o
}

func init() {
	once.Do(func() {
		singleton = (&Config{done: true}).init()
	})
}
